package ner;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DeclinationBasicRecognizer {

	
	
	
	private static final Integer AUX_WORD_WEIGHT = 1;
	private static final Integer MAX_TITLE_WEIGHT = 1000;
	private static final Integer MAX_ADJ_WEIGHT = 100;
	private static final Integer MAX_SUFFIX_WEIGHT = 10;
	private static final Integer NAME_AS_NOMINATIVE_WEIGHT = 20;


	private static Map<String,Case> auxiliaryWords= new HashMap<String, Case>(){{
		put("o",Case.LOCATIVE);
		put("z",Case.INSTRUMENTAL);
		put("pod",Case.INSTRUMENTAL);
		put("nad",Case.INSTRUMENTAL);
	}};
	
	private static Map<String,Map<Case,Integer>> MASCULINE_DECLINATION=new HashMap<String, Map<Case, Integer>>(){{
		put("a",new HashMap<Case,Integer>(){{
			put(Case.GENITIVE,1);
			put(Case.ACCUSATIVE,2);
			
			}}
		);
		put("owi",new HashMap<Case,Integer>(){{
			put(Case.DATIVE,1);
			}}
		);
		put("em",new HashMap<Case,Integer>(){{
			put(Case.INSTRUMENTAL,1);
			}}
		);	
		put("u",new HashMap<Case,Integer>(){{
			put(Case.LOCATIVE,1);
			put(Case.DATIVE,2);
			}}
		);
		put("e",new HashMap<Case,Integer>(){{
			put(Case.LOCATIVE,2);
			}}
		);	
	}};
	private static Map<String,Map<Case,Integer>> FEMININE_DECLINATION=new HashMap<String, Map<Case, Integer>>(){{
		put("i",new HashMap<Case,Integer>(){{
			put(Case.NOMINATIVE,2);
			put(Case.GENITIVE,1);
			put(Case.DATIVE,2);
			put(Case.LOCATIVE,2);
			
			}}
		);
		put("a",new HashMap<Case,Integer>(){{
			put(Case.NOMINATIVE,1);
			}}
		);
		put("e",new HashMap<Case,Integer>(){{
			put(Case.DATIVE,1);
			put(Case.LOCATIVE,1);
			
			}}
		);	
		put("ę",new HashMap<Case,Integer>(){{
			put(Case.ACCUSATIVE,1);
			
			}}
		);
		put("ą",new HashMap<Case,Integer>(){{
			put(Case.INSTRUMENTAL,1);
			}}
		);	
	}};
	//TODO neuter and plural
	
	Map<BookCharacter,List<DeclinationClueRecord>> getCluesFromPrecedingWords(ExtractionTask task){
		
		Map<Integer, List<BookCharacter>> sentencesWithCharacters=task.getSentencesWithCharacters();
		Map<BookCharacter,List<DeclinationClueRecord>>  bchDeclinationClues=new HashMap<BookCharacter,List<DeclinationClueRecord>>();
			
		
		for(Map.Entry<Integer, List<BookCharacter>> entry:sentencesWithCharacters.entrySet()){
			
			String sentence=task.getSentences().get(entry.getKey());
			List<String> words=Segmenter.splitByWords(sentence);
			
			for(BookCharacter bch:entry.getValue()){
				List<Integer> indexes=RecognitionUtil.findBchInSentenceAll(bch,words);
				for(Integer i:indexes){
					
					if(i>0){
						
						String word=words.get(i-1);
						String characterNameForm=words.get(i);
					//	System.out.println(word+"--"+characterNameForm);
						// polish grammar does not work in this way:)
						final Case caseFromAux=auxiliaryWords.get(word);
						if(caseFromAux!=null){
							
							final DeclinationClueRecord record=new DeclinationClueRecord(characterNameForm,caseFromAux, AUX_WORD_WEIGHT);
							if(bchDeclinationClues.containsKey(bch))
								bchDeclinationClues.get(bch).add(record);
							else
								bchDeclinationClues.put(bch, new ArrayList<DeclinationClueRecord>(Arrays.asList(record)));
							
						}
						
						if(PartOfSpeech.ADJECTIVE.equals(POStagger.getPOS(word))){
							
							
							List<Case> possibleCases=POStagger.getPossibleCasesOfAdjective(word,bch.getNumber(),Gender.convertToGrammaticalGender(bch.getGender()));
							for(Case possCase:possibleCases){
								
								Integer weight;
								if(possibleCases.size()==1)
									weight=MAX_ADJ_WEIGHT;
								else
									weight=1;
								
								final DeclinationClueRecord record=new DeclinationClueRecord(characterNameForm,possCase, weight);
								if(bchDeclinationClues.containsKey(bch))
									bchDeclinationClues.get(bch).add(record);
								else{
									bchDeclinationClues.put(bch, new ArrayList<DeclinationClueRecord>(Arrays.asList(record)));
									
								
								}
							}
										
						}
						if(bch.getTitles().keySet().contains(word)){
							List<Case> possibleCases=POStagger.getPossibleCasesOfNoun(word,bch.getNumber());
							for(Case possCase:possibleCases){
								
								Integer weight;
								if(possibleCases.size()==1)
									weight=MAX_TITLE_WEIGHT;
								else
									weight=1;
							
								
								final DeclinationClueRecord record=new DeclinationClueRecord(characterNameForm,possCase, weight);
								if(bchDeclinationClues.containsKey(bch))
									bchDeclinationClues.get(bch).add(record);
								else{
									bchDeclinationClues.put(bch, new ArrayList<DeclinationClueRecord>(Arrays.asList(record)));
									
								
								}
							}
							
						}
						
						
						
					}
					
					
				}
			
				
			}
			
			
			
		}
		
	
		System.out.println("\n\nABCD"+bchDeclinationClues.size());
		return bchDeclinationClues;
		
		
		
		
	}
	
	
	
	void recognizeDeclination(ExtractionTask task){
		
		Map<BookCharacter,List<DeclinationClueRecord>> clues=getCluesFromPrecedingWords(task);
		
	/*	for(Map.Entry<BookCharacter,List<DeclinationClueRecord>> entry : clues.entrySet()){
			
			System.out.println(entry.getKey().getName());
			for(DeclinationClueRecord rec:entry.getValue())
				System.out.println(rec);
			
			
		}
		*/
		
		for(BookCharacter bch:task.getCharacters()){
			
			List<DeclinationClueRecord> cluesFromSuffixes=recognizeDeclinationBySuffixes(bch);
			if(clues.get(bch)!=null){
				clues.get(bch).addAll(cluesFromSuffixes);
			}
			else{
				clues.put(bch, cluesFromSuffixes);
			}

			clues.get(bch).add(new DeclinationClueRecord(bch.getName(), Case.NOMINATIVE, NAME_AS_NOMINATIVE_WEIGHT));

		}
		
		for(Map.Entry<BookCharacter,List<DeclinationClueRecord>> entry:clues.entrySet()){
			
			Map<Case,Map<String,Integer>> ranking=new HashMap<Case, Map<String,Integer>>();
			
			for(DeclinationClueRecord record:entry.getValue()){
				
			
				if(ranking.get(record.getCaseRecognized())!=null){
					Integer valueForWord=ranking.get(record.getCaseRecognized()).get(record.getWord());
					if(valueForWord!=null){
		
						ranking.get(record.getCaseRecognized()).put(record.getWord(), valueForWord+record.getWeight());
						
					}
					else{
						ranking.get(record.getCaseRecognized()).put(record.getWord(), record.getWeight());
					}
					
					
				}
				else{
					Map<String,Integer> map=new HashMap<String,Integer>();
					map.put(record.getWord(),record.getWeight());
					ranking.put(record.getCaseRecognized(),map);
				}
					
				
			}
			
			
			Map<Case,String> declination=new HashMap<Case,String>();
			
			for(Map.Entry<Case,Map<String,Integer>> innerEntry:ranking.entrySet())
				declination.put(innerEntry.getKey(),Util.getMostFrequentFromMap(innerEntry.getValue()));
			
		
			
			entry.getKey().setDeclinationDetails(ranking);
			entry.getKey().setDeclination(declination);
			System.out.println(entry.getKey().getName());
			System.out.println(entry.getKey().getDeclinationDescription());
			for(Case c:ranking.keySet()){
				System.out.println(c);
				Util.showMap(ranking.get(c));
				
			}
			System.out.println("\n\n");
			

		}
		

		
		
		
	}
	
	

	private List<DeclinationClueRecord> recognizeDeclinationBySuffixes(BookCharacter bch) {
		
		List<DeclinationClueRecord> result=new ArrayList<DeclinationClueRecord>();
		Gender gender;
		Map<String,Map<Case,Integer>> suffixesMap=null;
		if((gender=bch.getGender())!=null){
			
			if(gender.equals(Gender.MALE)){
				suffixesMap=MASCULINE_DECLINATION;
			}
			else{
				suffixesMap=FEMININE_DECLINATION;
			}
			
		}
		else
			return result;
			
		
		for(String alias:bch.getAliases()){
			
			for(Map.Entry<String,Map<Case,Integer>> entry:suffixesMap.entrySet()){
				if(alias.endsWith(entry.getKey())){
					for(Map.Entry<Case, Integer> innerEntry:entry.getValue().entrySet()){
						DeclinationClueRecord record=new DeclinationClueRecord(alias, innerEntry.getKey(), MAX_SUFFIX_WEIGHT/innerEntry.getValue());
						result.add(record);
					}
				}
			}
		}
		
		return result;
		
	}
	
	
	
	
}


class DeclinationClueRecord{
	
	private String word;
	private Case caseRecognized;
	private Integer weight;

	
	public String getWord() {
		return word;
	}


	public Case getCaseRecognized() {
		return caseRecognized;
	}


	public Integer getWeight() {
		return weight;
	}


	public DeclinationClueRecord(String word, Case caseRecognized, Integer weight) {
		this.word=word;
		this.caseRecognized=caseRecognized;
		this.weight=weight;
	}
	
	public String toString(){
		return word+":"+caseRecognized+":"+weight;
		
	}
	
	
}


